package cn.alex.sso.project.sync;

import cn.alex.sso.project.entity.SysClient;
import cn.alex.sso.project.entity.SysClientSyncUrl;
import cn.alex.sso.project.service.SysClientService;
import cn.alex.sso.project.service.SysClientSyncService;
import cn.alex.sso.project.sync.entity.DataSyncResult;
import cn.alex.sso.project.sync.event.AbstractDataSyncEvent;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
import cn.hutool.crypto.symmetric.SymmetricCrypto;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.dtflys.forest.Forest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;

/**
 * 数据同步事件监听器
 *
 * @author Alex
 * @date 2024/12/1 02:40
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class DataSyncListener {
    private final SysClientService clientService;
    private final SysClientSyncService clientSyncService;

    @EventListener
    public void onEventListener(AbstractDataSyncEvent event) {
        Object data = event.getData();
        String module = event.getModule();
        String trigger = event.getTrigger();


        // 需要发送的原始数据数据
        DataSyncResult requestBody = new DataSyncResult();
        requestBody.setModule(module);
        requestBody.setTrigger(trigger);
        requestBody.setData(event.customProcess(data));

        dataSync(event, module, trigger, requestBody);
    }

    public void dataSync(AbstractDataSyncEvent event, String module, String trigger, DataSyncResult requestBody) {
        ThreadUtil.execute(() -> {
            // 查询需要同步的应用
            List<SysClientSyncUrl> needSyncClient = clientSyncService.getNeedSyncClient(event.customSyncClient(), module, trigger);
            if (CollUtil.isEmpty(needSyncClient)) {
                return;
            }

            for (SysClientSyncUrl item : needSyncClient) {
                SysClient client = clientService.getOne(new LambdaQueryWrapper<SysClient>()
                        .select(SysClient::getId, SysClient::getName, SysClient::getSecretKey)
                        .eq(SysClient::getId, item.getClientId())
                );

                if (ObjUtil.isEmpty(client)) {
                    continue;
                }

                // 使用应用密钥对发送的数据进行加密
                byte[] key = client.getSecretKey().getBytes(StandardCharsets.UTF_8);
                byte[] aesKey = new byte[16];
                System.arraycopy(key, 0, aesKey, 0, 16);

                SymmetricCrypto aes = new SymmetricCrypto(SymmetricAlgorithm.AES, aesKey);
                String encryptHex = aes.encryptHex(JSONUtil.toJsonStr(requestBody));

                ThreadUtil.execute(() -> {
                    try {
                        log.info(StrUtil.format("数据同步中，应用（{}）ID：{}", client.getId(), client.getName()));
                        String resultBody = Forest.post(item.getSyncUrl())
                                .addHeader("Content-Type", "application/json")
                                .addBody(StrUtil.format("{\"data\":\"{}\"}", encryptHex))
                                .executeAsString();
                        log.info(StrUtil.format("数据同步成功，应用（{}）ID：{}，结果：{}", client.getId(), client.getName(), resultBody));
                    } catch (Exception e) {
                        log.error(StrUtil.format("数据同步失败，应用（{}）ID：{}", client.getId(), client.getName()), e);
                    }
                });
            }
        });
    }

    public static void main(String[] args) {
        HttpUtil.createServer(8888)
                .addAction("/sync", (req, res) -> {
                    String body = req.getBody();
                    Map bean = JSONUtil.toBean(body, Map.class);

                    byte[] key = "d31c11d989b443ad916588c8b1e923e2".getBytes(StandardCharsets.UTF_8);
                    byte[] aesKey = new byte[16];
                    System.arraycopy(key, 0, aesKey, 0, 16);

                    SymmetricCrypto aes = new SymmetricCrypto(SymmetricAlgorithm.AES, aesKey);

                    String data = aes.decryptStr(MapUtil.getStr(bean, "data"));
                    res.write(data);
                })
                .start();
    }
}
